%%%====================================================================
%%% File    : iter.erl
%%% Author  :  <vjache>
%%% Description : This module implements iteration logic. It uses the 
%%% 			  iterator descriptor defined at 'iter.hrl'.
%%%
%%% Created : 13 Jan 2009 by  <vjache>
%%%====================================================================
-module(iter).

%% API
-export([	open/1,
       		next/1,
        	close/1,
        	foreach/2,
			fold/3,
			from_module/2,
            from_list/1,
			to_list/1,
			to_rlist/1,
			empty/0,
			single/1,
			simple_immutable/3,
			fun_factory/1,
			print/1]).
%%
%% Include files
%%
-include("../include/iter.hrl").

%%====================================================================
%% API
%%====================================================================

%%--------------------------------------------------------------------
%% Function: 	open(Iter) -> IterState
%% Description: Opens an iterator by descriptor. Accepts an iterator
%%				description record and returns an initial state of
%%				iteration.
%%--------------------------------------------------------------------
open(#iter{param=Param,impl=Mod}) when is_atom(Mod) ->
	{Mod,Mod:open(Param)};
open(#iter{param=Param,impl=FunSet}) 
  when is_record(FunSet,fun_set) -> %%IterState
    OpenFun=FunSet#fun_set.open_fun,
	{FunSet,OpenFun(Param)}.
%%--------------------------------------------------------------------
%% Function: 	next(IterState) -> {NewIterState,Item}|{NewIterState,'$EOF'}
%% Description: Takes a next item from iterator and moves the iterator 
%%				state further. Accepts a current iterator state and 
%%				returns a pair of iterator new state and item.
%%--------------------------------------------------------------------
next({Mod,State}) when is_atom(Mod) ->
	case Mod:next(State) of
		{FinalState,?EOF} ->
			{{Mod,FinalState},?EOF};
		{NewState,Item} ->
			{{Mod,NewState},Item}
	end;
next({FunSet,State}) ->
    NextFun=FunSet#fun_set.next_fun,
	case NextFun(State) of
		{FinalState,?EOF} ->
			{{FunSet,FinalState},?EOF};
		{NewState,Item} ->
			{{FunSet,NewState},Item}
	end.
%%--------------------------------------------------------------------
%% Function: 	close(IterState) -> ok
%% Description: Closes an iterator. After this call the iterator state 
%%				could not be used in such a calls like next, foreach, etc.
%%--------------------------------------------------------------------
close({Mod,State}) when is_atom(Mod) ->
	Mod:close(State),ok;
close({FunSet,State}) ->
	CloseFun=FunSet#fun_set.close_fun,
	CloseFun(State),
	ok.
%%--------------------------------------------------------------------
%% Function: 	foreach(IterSate,Fun) -> {NewIterState,ok}
%% Description: Analogus to lists:foreach/2. Evaluates Fun for each 
%%				item in iterator. Accepts Iterator state, user function
%%				and returns pair of iterator state and atom 'ok'.
%%--------------------------------------------------------------------
foreach({Mod,State},Fun) when is_atom(Mod) andalso is_function(Fun)->
    {FinalState,ok}=foreach_raw(State,Mod,Fun),
	{{Mod,FinalState},ok};
foreach({FunSet,State},Fun) when is_record(FunSet,fun_set) andalso is_function(Fun)->
	NextFun=FunSet#fun_set.next_fun,
    {FinalState,ok}=foreach_raw(State,NextFun,Fun),
	{{FunSet,FinalState},ok};
foreach(Iter,Fun) when is_record(Iter,iter) andalso is_function(Fun)->
    foreach(open(Iter),Fun).

%%--------------------------------------------------------------------
%% Function: 	fold(IterState,Fun,Acc0) -> {NewIterState,AccOut}
%% Description: Analogus to lists:foldl/3. 
%%--------------------------------------------------------------------
fold({Mod,State},Fun,AccIn) when is_atom(Mod) andalso is_function(Fun)->
    {FinalState,AccTot}=fold_raw(State,Mod,Fun,AccIn),
	{{Mod,FinalState},AccTot};
fold({FunSet,State},Fun,AccIn) when is_record(FunSet,fun_set) andalso is_function(Fun)->
    NextFun=FunSet#fun_set.next_fun,
    {FinalState,AccTot}=fold_raw(State,NextFun,Fun,AccIn),
	{{FunSet,FinalState},AccTot};
fold(Iter,Fun,AccIn) when is_record(Iter,iter) and is_function(Fun)->
    fold(open(Iter),Fun,AccIn).

%%--------------------------------------------------------------------
%% Function: 	from_module(Mod,Param) -> iter()
%% Description: Creates an iterator descriptor using specified callback 
%%				module and parameter to be passed to Mod:open(Param) call.
%%--------------------------------------------------------------------
from_module(Mod,Param)->
    #iter{param=Param,impl=Mod}.

simple_immutable(OpenFun, NextFun, CloseFun) 
  when is_function(OpenFun, 0) andalso
		   is_function(NextFun, 1) andalso
		   is_function(CloseFun, 1) ->
	#iter{param=simple_immutable,
		  impl=#fun_set{
						open_fun=fun(_)-> OpenFun() end,
						next_fun=fun(S)-> {S,NextFun(S)} end,
						close_fun=fun(S)-> CloseFun(S),ok end
					   }
		 }.
fun_factory(FactoryFun) 
  when is_function(FactoryFun, 0) ->
	#iter{param=fun_result,
		  impl=#fun_set{
						open_fun=fun(_)-> iter:open(FactoryFun()) end,
						next_fun=fun(S)-> iter:next(S) end,
						close_fun=fun(S)-> iter:close(S) end
					   }
		 }.
%%--------------------------------------------------------------------
%% Function: 	from_list(List) -> iter()
%% Description: Creates an iterator descriptor for list.
%%--------------------------------------------------------------------
from_list(List) ->
	iter_list:new(List).
%%--------------------------------------------------------------------
%% Function: 	empty() -> iter()
%% Description: Creates an iterator descriptor for empty collection.
%%--------------------------------------------------------------------
empty() ->
	#iter{param=empty,
		  impl=#fun_set{
						open_fun=fun(empty)-> empty end,
						next_fun=	fun	(empty) -> 
											 {empty,?EOF}
									end,
						close_fun=fun(empty)-> ok end
					   }
		 }.
%%--------------------------------------------------------------------
%% Function: 	single(Item) -> iter()
%% Description: Creates an iterator descriptor for single valued collection.
%%--------------------------------------------------------------------
single(Item) ->
	#iter{param=single,
		  impl=#fun_set{
						open_fun=fun(single)-> single end,
						next_fun=	fun	(single) -> 
											 {?EOF,Item};
									   (?EOF) ->
											{?EOF,?EOF}
									end,
						close_fun=fun(_)-> ok end
					   }
		 }.

print(IterOrState) ->
	{FinState,_Tot}=fold(IterOrState,
						 fun(Item,Cnt)->
								 io:format("~p#~p ~p~n",[self(),Cnt,Item]),
								 Cnt+1 
						 end,0),
	close(FinState).

to_list(IterOrState) ->
	lists:reverse(to_rlist(IterOrState)).
to_rlist(IterOrState) ->
	{State,Acc}=fold(IterOrState,
		 fun(Item,AccIn) ->
				 [Item|AccIn]
		 end, []),
	iter:close(State),
	Acc.
%%====================================================================
%% Internal functions
%%====================================================================
fold_raw(State,Mod,Fun,AccIn) when is_atom(Mod)->
	case Mod:next(State) of
		{FinalState,?EOF} ->
			{FinalState,AccIn};
		{NewState,Item} ->
			AccOut=Fun(Item,AccIn),
			fold_raw(NewState,Mod,Fun,AccOut)
	end;
fold_raw(State,NextFun,Fun,AccIn) ->
	case NextFun(State) of
		{FinalState,?EOF} ->
			{FinalState,AccIn};
		{NewState,Item} ->
			AccOut=Fun(Item,AccIn),
			fold_raw(NewState,NextFun,Fun,AccOut)
	end.

foreach_raw(State,Mod,Fun) when is_atom(Mod) ->
	case Mod:next(State) of
		{FinalState,?EOF} ->
			{FinalState,ok};
		{NewState,Item} ->
			Fun(Item),
			foreach_raw(NewState,Mod,Fun)
	end;
foreach_raw(State,NextFun,Fun) ->
	case NextFun(State) of
		{FinalState,?EOF} ->
			{FinalState,ok};
		{NewState,Item} ->
			Fun(Item),
			foreach_raw(NewState,NextFun,Fun)
	end.